package model;


import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Random;

import model.GA.MutationEnum;

import selection.Selection;
import crossover.Crossover;

public class Algorythm implements Runnable {

	Selection selection;
	Crossover crossover;
	ArrayList<Chromosome> population;
	int generations;
	ArrayList<ArrayList<Chromosome>> statistics;
	Random rand;
	double mutationRate;
	int elitismNumber;
	boolean printChromosome;
	static String seperator = System.getProperty("file.separator");
	static String root = "results" + seperator;
	int populationSize;
	MutationEnum myMutation=MutationEnum.RSM;
	public boolean isPrintChromosome() {
		return printChromosome;
	}

	public void setPrintChromosome(boolean printChromosome) {
		this.printChromosome = printChromosome;
	}

	public Algorythm(Selection selection, Crossover crossover,
			ArrayList<Chromosome> population, int generations,
			double mutationRate, int elitismNumber) {
		super();
		this.selection = selection;
		this.crossover = crossover;
		this.population = population;
		this.generations = generations;
		this.mutationRate = mutationRate;
		this.elitismNumber = elitismNumber;
		this.populationSize = population.size();

		statistics = new ArrayList<ArrayList<Chromosome>>();
		statistics.add(population);
		rand = new Random();
	}

//	@Override
//	public void run() {
//		Chromosome[] parents;
//		Chromosome[] childs;
//		int genCounter = 0;
//		for (int i = 0; i < generations; i++) {
//			if (genCounter < i) {
//				System.out.println("generation:" + i);
//				genCounter = genCounter + 100;
//			}
//			ArrayList<Chromosome> newPopulation = new ArrayList<Chromosome>();
//
//			elitism(newPopulation);
//
//			while (newPopulation.size() < population.size()) {
//				parents = selection.selectPatents(population);
//				childs = crossover.crossover(parents);
//				if (childs.length == 2) {
//					newPopulation.add(childs[0]);
//					newPopulation.add(childs[1]);
//
//				} else {
//					System.out.println("crossover didnt made well");
//				}
//			}
//			population = newPopulation;
//			mutationRSM();
////			mutation1();
//			statistics.add(population);
//		}
//	}
	
	@Override
	public void run() {
		Chromosome[] parents;
		Chromosome[] childs;
		int genCounter=0;
		for (int i = 0; i < generations; i++) {
			if(genCounter<i){
				System.out.println("generation:"+i);
				genCounter=genCounter+100;
			}
			ArrayList<Chromosome> newPopulation = new ArrayList<Chromosome>();
			while (newPopulation.size() < population.size()-this.elitismNumber) {
				parents = selection.selectPatents(population);
				childs = crossover.crossover(parents);
				if (childs.length == 2) {
					newPopulation.add(childs[0]);
					newPopulation.add(childs[1]);

				} else {
					System.out.println("crossover didnt made well");
				}
			}
			ArrayList<Chromosome> oldpopulation=population;
			population = newPopulation;
			if(myMutation==MutationEnum.RANDOM)
				mutation1();
			else if(myMutation==MutationEnum.SWAP)
				mutation();
			else
				mutationRSM();
			elitism(newPopulation,oldpopulation);
			statistics.add(population);
			
		}
	}

	public MutationEnum getMyMutation() {
		return myMutation;
	}

	public void setMyMutation(MutationEnum myMutation) {
		this.myMutation = myMutation;
	}

	public ArrayList<ArrayList<Chromosome>> getStatistics() {
		return statistics;
	}

	public void mutation() {

		for (Chromosome chromosome1 : population) {
			double index = rand.nextDouble();
			ArrayList<Integer> chromosome = chromosome1.getChromosome();
			int size = chromosome.size();
			if (index > mutationRate) {
				int indexToChange1 = rand.nextInt(size);
				int indexToChange2 = rand.nextInt(size);

				Integer temp = chromosome.get(indexToChange1);
				chromosome.set(indexToChange1, chromosome.get(indexToChange2));
				chromosome.set(indexToChange2, temp);
				chromosome1.calcFitness();
			}

		}

	}

	public void mutation1() {
		for (Chromosome chromosome1 : population) {
			double index = rand.nextDouble();
			ArrayList<Integer> chromosome = chromosome1.getChromosome();
			int size = chromosome.size();
			if (index > mutationRate) {
				int indexToChange1 = rand.nextInt(size);
				
				Integer temp = chromosome.get(indexToChange1);
				chromosome.remove(temp);
				int indexToChange2 = rand.nextInt(size-1);
				chromosome.add(indexToChange2,temp);
				chromosome1.calcFitness();
			}

		}

	}
	public void mutationRSM() {

		for (Chromosome chromosome1 : population) {
			double index = rand.nextDouble();
			ArrayList<Integer> chromosome = chromosome1.getChromosome();
			int size = chromosome.size();
			if (index > mutationRate) {
				int indexToChange1 = rand.nextInt(size);
				int indexToChange2 = rand.nextInt(size);
				if(indexToChange2<indexToChange1){
					int index3=indexToChange1;
					indexToChange1=indexToChange2;
					indexToChange2=index3;
				}
				int sizeOfinvert=((indexToChange2-indexToChange1)/2);	
				for(int i=0;i<sizeOfinvert;i++){
					Integer temp = chromosome.get(indexToChange1+i);
					chromosome.set(indexToChange1+i, chromosome.get(indexToChange2-i));
					chromosome.set(indexToChange2-i, temp);
				}
				chromosome1.calcFitness();
			}

		}

	}
	public void printStatistics() {
		int i = 1;
		Chromosome max = null;
		StringBuilder builder = new StringBuilder();
		builder.append(root);
		builder.append(crossover.toString()).append("With")
				.append(selection.toString()).append("With");
		if(myMutation==MutationEnum.SWAP)
			builder.append("SWAP");
		else if(myMutation==MutationEnum.RSM)
			builder.append("RSM");
		else
			builder.append("Random");
	
		File file = new File(builder.toString());
		file.mkdirs();
		builder.append(seperator);
		builder.append(seperator);
		builder.append("eliteNumber=");
		builder.append(elitismNumber);
		builder.append(",mutationRate=");
		builder.append(mutationRate);
		builder.append(",generation=");
		builder.append(statistics.size());
		builder.append(",population=");
		builder.append(this.populationSize);
		builder.append(".csv");
		FileWriter fstream = null;
		BufferedWriter out = null;
		try {
			System.out.println("results at:" + builder.toString());
			fstream = new FileWriter(builder.toString());
			out = new BufferedWriter(fstream);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		builder = new StringBuilder();
		for (ArrayList<Chromosome> arr : statistics) {
			double avg = 0;
			for (Chromosome chromosome : arr) {
				avg += chromosome.getFitness();
			}
			avg /= arr.size();
			max = getBest(arr);
			// System.out.println("gen no: "+i+" [size="+arr.size()+" avgfit:"+avg+" best:"+max.getFitness()+"]");

			try {
				builder = new StringBuilder();
				builder.append(i).append(",").append(avg).append(",")
						.append(max.getFitness());
				if (printChromosome)
					builder.append(",").append(max.getChromosome());
				builder.append('\n');
				out.write(builder.toString());
//				System.out.println(builder.toString());
			} catch (IOException e) {
				e.printStackTrace();
			}
			++i;
		}
		try {
			out.flush();
			out.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public Chromosome getBest(ArrayList<Chromosome> arr) {
		Chromosome max = arr.get(0);
		for (Chromosome chromosome : arr) {
			if (max.getFitness() < chromosome.getFitness())
				max = chromosome;
		}
		return max;
	}

	public void elitism(ArrayList<Chromosome> newPopulation) {
		ArrayList<Chromosome> copiedPopulation = new ArrayList<Chromosome>();
		for (Chromosome chromosome : population) {
			copiedPopulation.add(chromosome);
		}
		Collections.sort(copiedPopulation, new Comparator<Chromosome>() {

			@Override
			public int compare(Chromosome o2, Chromosome o1) {
				return (int) o1.getFitness() - (int) o2.getFitness();
			}
		});
		for (int i = 0; i < elitismNumber; i++) {
			newPopulation.add(new Chromosome(copiedPopulation.get(i)
					.getChromosome()));
		}

	}
	public void elitism(ArrayList<Chromosome> newPopulation, ArrayList<Chromosome> oldPopulation) {
		ArrayList<Chromosome> copiedPopulation=new ArrayList<Chromosome>();
		for (Chromosome chromosome : oldPopulation) {
			copiedPopulation.add(chromosome);
			
		}
		Collections.sort(copiedPopulation, new Comparator<Chromosome>() {

			@Override
			public int compare(Chromosome o2, Chromosome o1) {
				return (int) o1.getFitness() - (int) o2.getFitness();
			}
		});
		for (int i = 0; i < elitismNumber; i++) {
			newPopulation.add(new Chromosome(copiedPopulation.get(i).getChromosome()));
		}
		
	}

}
